//
// Created by hjie on 23-12-27.
//


#pragma once
#include <cstring>
#include <string>
#include <memory>
#include <functional>
#include "codec_config.h"
extern "C"
{
#include <libavutil/avutil.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/frame.h>
#include <libavutil/buffer.h>
#include <libavutil/hwcontext.h>
#include <libavfilter/avfilter.h>
#include <libavutil/pixfmt.h>
#include <libavutil/pixdesc.h>
}

class MediaStream;

class DecoderContext
{
private:
    friend class InputFormatContext;
    friend class OutputFormatContext;
public:
    DecoderContext();
    ~DecoderContext();
    bool CreateDecoderContextByName(const std::string & name, const std::string & hw_name = "");
    bool CreateDecoderContextById(AVCodecID codec_id, const std::string & hw_name = "");
    void ConfigParam(std::shared_ptr<MediaStream> & media_stream);
    bool OpenCodecCtx();
    bool ProcessHwFrameStatus();
    void HwFrameFunc(std::function<bool()> && callback_func);
    AVCodecContext * AvCodecCtx();
    bool ConfigHw(const std::string & hw_name);
public:
    int GetHwMethod();
    bool GetHwInitStatus();
private:
    const AVCodec * FindCodecByName(const std::string & name);
    const AVCodec * FindCodecByNameAndHw(const std::string & name, const std::string & hw_name = "");
    const AVCodec * FindCodecByIdAndHw(AVCodecID codec_id);
    const AVCodec * FindCodecById(AVCodecID codec_id);
    const AVCodec * FindCodecByIdAndHw(AVCodecID codec_id, AVHWDeviceType hw_device_type);
    const AVCodec * FindCodecByCustomFunc(std::function<bool(const AVCodec * ptr)> && func);
    bool  ProcessHwConfig();
private:
    AVCodecContext * m_decoder_ctx;
private:
    bool m_hw_frame_init_status{false};
    const AVCodecHWConfig * m_codec_hw_config;
    AVBufferRef           * m_buffer_ref;
    std::function<bool()>   m_hw_frane_callback;
private:
    std::shared_ptr<MediaStream> m_media_stream;
};

class EncoderContext
{
private:
    friend class InputFormatContext;
    friend class OutputFormatContext;
public:
    EncoderContext();
    ~EncoderContext();
    bool CreateEncoderContextByName(const std::string & name, const std::string & hw_name = "");
    bool CreateEncoderContextById(AVCodecID codec_id, const std::string & hw_name = "");
    bool OpenCodecCtx();
    AVCodecContext * AvCodecCtx();
    int  GetHwMethod();
    bool GetHwInitStatus();
    void SetAvBufferRef(AVBufferRef * ref_buffer);
    void ConfigParam(const VideoParamConfig & param_config);
    void ConfigParam(const AudioParamConfig & param_config);
private:
    const AVCodec * FindCodecByName(const std::string & name);
    const AVCodec * FindCodecById(AVCodecID codec_id);
    const AVCodec * FindCodecByNameAndHw(const std::string & name, const std::string & hw_name = "");
    const AVCodec * FindCodecByIdAndHw(AVCodecID codec_id, const std::string & hw_name);
    const AVCodec * FindCodecByCusttomFunc(std::function<bool(const AVCodec * ptr)> && func);
    bool ProcessHwConfig();
private:
    AVCodecContext * m_encoder_ctx{nullptr};
private:
    bool m_hw_frame_init_status{false};
    const AVCodecHWConfig * m_codec_hw_config{nullptr};
};


